package com.alibaba.cloud.ddd.domain.model.cargo;

import java.util.Date;

import com.alibaba.cloud.ddd.domain.model.location.Location;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import com.alibaba.cloud.ddd.domain.shared.AbstractSpecification;
import com.alibaba.cloud.ddd.domain.shared.ValueObject;

/**
 * Route specification. Describes where a cargo origin and destination is, and
 * the arrival deadline.
 * 
 */
@Embeddable
public class RouteSpecification extends AbstractSpecification<Itinerary> implements ValueObject<RouteSpecification> {

	/**
	 * 
	 */
	private static final long serialVersionUID = 644169687309174988L;

	@ManyToOne()
	@JoinColumn(name = "spec_origin_id")
	public Location origin;

	@ManyToOne()
	@JoinColumn(name = "spec_destination_id")
	public Location destination;

	@Column(name = "spec_arrival_deadline", nullable = false)
	public Date arrivalDeadline;

	/**
	 * @param origin          origin location - can't be the same as the destination
	 * @param destination     destination location - can't be the same as the origin
	 * @param arrivalDeadline arrival deadline
	 */
	public RouteSpecification(final Location origin, final Location destination, final Date arrivalDeadline) {
		Validate.notNull(origin, "Origin is required");
		Validate.notNull(destination, "Destination is required");
		Validate.notNull(arrivalDeadline, "Arrival deadline is required");
		Validate.isTrue(!origin.sameIdentityAs(destination), "Origin and destination can't be the same: " + origin);

		this.origin = origin;
		this.destination = destination;
		this.arrivalDeadline = (Date) arrivalDeadline.clone();
	}

	/**
	 * @return Specified origin location.
	 */
	public Location origin() {
		return origin;
	}

	/**
	 * @return Specfied destination location.
	 */
	public Location destination() {
		return destination;
	}

	/**
	 * @return Arrival deadline.
	 */
	public Date arrivalDeadline() {
		return new Date(arrivalDeadline.getTime());
	}

	@Override
	public boolean isSatisfiedBy(final Itinerary itinerary) {
		return itinerary != null && origin().sameIdentityAs(itinerary.initialDepartureLocation())
				&& destination().sameIdentityAs(itinerary.finalArrivalLocation())
				&& arrivalDeadline().after(itinerary.finalArrivalDate());
	}

	@Override
	public boolean sameValueAs(final RouteSpecification other) {
		return other != null
				&& new EqualsBuilder().append(this.origin, other.origin).append(this.destination, other.destination)
						.append(this.arrivalDeadline, other.arrivalDeadline).isEquals();
	}

	@Override
	public boolean equals(final Object o) {
		if (this == o)
			return true;
		if (o == null || getClass() != o.getClass())
			return false;

		final RouteSpecification that = (RouteSpecification) o;

		return sameValueAs(that);
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder().append(this.origin).append(this.destination).append(this.arrivalDeadline)
				.toHashCode();
	}

	RouteSpecification() {
		// Needed by Hibernate
	}

}
